import os.path
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from docx import Document
import re
import sys
import inspect
from test_tools.class_excel import Writer
from inter_keys import HTTP  # 引入关键字类

try:
    from docx import Document
except ImportError:
    messagebox.showerror("依赖错误", "缺少python-docx库，请执行：\npip install python-docx")
    sys.exit()


class APITestGenerator:
    def __init__(self, root):
        self.root = root
        self.root.title("智能接口测试用例生成器")
        self.root.geometry("600x400")

        # 配置信息
        self.template_file = os.path.abspath("../test_cases/ai_接口用例.xlsx")
        self.output_file = os.path.abspath("../test_cases/Generated_HTTP接口用例.xlsx")

        # 获取关键字列表
        self.keywords = self.get_keywords()
        self.base_written = False  # 标记基础设置是否已写入
        self.relations = {}  # 存储关联数据

        # 初始化界面
        self.create_widgets()
        self.interfaces = []

        # 样式配置
        style = ttk.Style()
        style.configure("TButton", padding=6)
        style.configure("TLabel", padding=6)

    def get_keywords(self):
        """获取inter_keys中的关键字方法"""
        methods = []
        for name, _ in inspect.getmembers(HTTP, predicate=inspect.isfunction):
            if not name.startswith('_'):  # 排除私有方法
                methods.append(name)
        return methods

    def create_widgets(self):
        """创建界面组件"""
        main_frame = ttk.Frame(self.root)
        main_frame.pack(padx=20, pady=20, fill=tk.BOTH, expand=True)

        # 文件上传区域
        file_frame = ttk.LabelFrame(main_frame, text="文档处理")
        file_frame.pack(fill=tk.X, pady=10)

        self.btn_load = ttk.Button(file_frame, text="加载接口文档", command=self.load_document)
        self.btn_load.pack(side=tk.LEFT, padx=10, pady=5)

        self.btn_generate = ttk.Button(file_frame, text="生成测试用例",
                                       command=self.generate_cases, state=tk.DISABLED)
        self.btn_generate.pack(side=tk.LEFT, padx=10, pady=5)

        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="生成日志")
        log_frame.pack(fill=tk.BOTH, expand=True)

        self.log_text = tk.Text(log_frame, height=10, state=tk.DISABLED)
        scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=scrollbar.set)

        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    def log(self, message):
        """记录日志信息"""
        self.log_text.configure(state=tk.NORMAL)
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.configure(state=tk.DISABLED)
        self.log_text.see(tk.END)

    def load_document(self):
        """加载Word文档"""
        doc_path = filedialog.askopenfilename(
            filetypes=[("Word文档", "*.docx")],
            title="选择接口文档"
        )
        if doc_path:
            try:
                self.parse_document(doc_path)
                self.btn_generate.config(state=tk.NORMAL)
                self.log("文档解析完成，发现接口：" + ", ".join([i["name"] for i in self.interfaces]))
            except Exception as e:
                messagebox.showerror("解析错误", f"文档解析失败：{str(e)}")

    def parse_document(self, doc_path):
        """解析Word文档结构"""
        doc = Document(doc_path)
        self.interfaces = []
        current_interface = None

        interface_pattern = re.compile(r"^\d+、(\w+)$")
        section_patterns = {
            "path": re.compile(r"请求路径：(.+)"),
            "method": re.compile(r"请求方法：(.+)"),
            "params": re.compile(r"请求参数："),
            "response": re.compile(r"返回参数说明："),
            "desc": re.compile(r"功能说明：\s*>(.+?)<", re.DOTALL)  # 提取功能说明作为用例标题
        }

        for para in doc.paragraphs:
            text = para.text.strip()

            # 识别接口标题
            interface_match = interface_pattern.match(text)
            if interface_match:
                if current_interface:
                    self.interfaces.append(current_interface)
                current_interface = {
                    "name": interface_match.group(1),
                    "method": "POST",
                    "path": "",
                    "desc": "",  # 功能说明作为用例标题
                    "params": [],
                    "responses": [],
                    "status_codes": []
                }
                continue

            # 解析功能说明
            if current_interface and not current_interface["desc"]:
                desc_match = section_patterns["desc"].search(text)
                if desc_match:
                    current_interface["desc"] = desc_match.group(1).strip()

            # 解析其他字段
            if current_interface:
                for field, pattern in section_patterns.items():
                    if field == "desc":
                        continue
                    match = pattern.search(text)
                    if match:
                        if field == "path":
                            current_interface["path"] = match.group(1)
                        elif field == "method":
                            current_interface["method"] = match.group(1).upper()
                        elif field == "params":
                            self.parse_parameters(current_interface, doc)
                        elif field == "response":
                            self.parse_responses(current_interface, doc)
                        break

        if current_interface:
            self.interfaces.append(current_interface)

    def parse_parameters(self, interface, doc):
        """解析参数表格"""
        for table in doc.tables:
            headers = [cell.text.strip() for cell in table.rows[0].cells]
            if "参数名称" in headers and "是否必须" in headers:
                for row in table.rows[1:]:
                    cells = [cell.text.strip() for cell in row.cells]
                    if len(cells) >= 4:
                        interface["params"].append({
                            "name": cells[0],
                            "required": cells[1],
                            "type": cells[2],
                            "desc": cells[3]
                        })
                break

    def parse_responses(self, interface, doc):
        """解析响应参数"""
        for table in doc.tables:
            headers = [cell.text.strip() for cell in table.rows[0].cells]
            if "参数名称" in headers and "类型" in headers:
                for row in table.rows[1:]:
                    cells = [cell.text.strip() for cell in row.cells]
                    if len(cells) >= 3 and cells[0] == "status":
                        interface["status_codes"].append({
                            "code": cells[1],
                            "desc": cells[2]
                        })

    def generate_cases(self):
        """生成测试用例"""
        try:
            # 检查输出文件是否存在
            if os.path.exists(self.output_file):
                if not messagebox.askyesno("文件存在", "输出文件已存在，是否删除并重新生成？"):
                    self.log("在原有文件追加用例...")
                else:
                    os.remove(self.output_file)
                    self.log("已删除旧文件，将生成新文件...")

            writer = Writer()
            writer.copy_open(self.template_file, self.output_file)

            # 先写入基础设置
            self.write_base_config(writer)

            # 收集所有正常和异常流程
            normal_cases = []
            error_cases = []

            for interface in self.interfaces:
                # 生成用例标题
                case_title = interface["desc"].split('。')[0] if interface["desc"] else interface["name"]

                # 收集正常流程
                normal = self.build_normal_flow(interface, case_title)
                if normal:
                    normal_cases.extend(normal)

                # 收集异常流程
                error = self.build_error_flow(interface, case_title)
                if error:
                    error_cases.extend(error)
            if normal_cases:
                normal_cases.insert(0, ["", "正常流程", "", "", "", "", "", "", "", ""])
            if error_cases:
                error_cases.insert(0, ["", "异常流程", "", "", "", "", "", "", "", ""])

            # 写入所有正常流程
            self.write_excel_data(writer, "接口流程", normal_cases)

            # 写入所有异常流程
            self.write_excel_data(writer, "接口流程", error_cases)

            writer.save_close()
            self.log(f"用例生成完成，已保存到：{self.output_file}")
            messagebox.showinfo("完成", "测试用例生成成功！")
        except Exception as e:
            messagebox.showerror("生成错误", str(e))

    def write_base_config(self, writer):
        """写入基础配置（仅执行一次）"""
        if self.base_written:
            return

        base_url = "http://192.168.0.100:38080/inter/HTTP/"
        data = [
            ["", "基础设置", "", "", "", "", "", "", "", ""],
            ["", "", "", "设置地址", "set_url", base_url, "", "", "", ""],
            ["", "", "", "添加请求头", "add_header", "Content-Type", "application/json", "", "", ""]
        ]
        self.write_excel_data(writer, "接口流程", data)
        self.base_written = True

    def build_normal_flow(self, interface, case_title):
        """构建正常流程数据"""
        case_data = []
        # 用例标题行
        case_data.append(["", "", case_title, "", "", "", "", "", "", ""])

        # 请求步骤
        payload = self.build_payload(interface["params"], valid=True)
        case_data.append(["", "", "", interface["name"], "post", interface["path"], payload, "", "", ""])

        # 自动处理关联数据
        if "token" in interface["name"].lower():
            case_data.append(["", "", "", "保存token", "save_relation", "token", "token", "", "", ""])
            case_data.append(["", "", "", "添加请求头", "add_header", "token", "{{token}}", "", "", ""])

        # 断言
        case_data.append(["", "", "", "断言状态码", "assert_equal", "status", "200", "", "", ""])
        case_data.append(["", "", "", "断言响应数据", "assert_contains", "success", "", "", "", ""])

        return case_data

    def build_error_flow(self, interface, case_title):
        """构建异常流程数据"""
        case_data = []
        # 异常流程分组行
        case_data.append(["", "异常流程", "", "", "", "", "", "", "", ""])
        # 用例标题行
        case_data.append(["", "", f"{case_title}-异常", "", "", "", "", "", "", ""])

        # 异常测试
        payload = self.build_payload(interface["params"], valid=False)
        case_data.append(["", "", "", "参数缺失测试", "post", interface["path"], payload, "", "", ""])
        case_data.append(["", "", "", "断言错误信息", "assert_contains", "error", "", "", "", ""])

        return case_data

    def build_payload(self, params, valid=True):
        """构建请求参数（自动处理关联数据）"""
        payload = []
        for param in params:
            if param["required"] == "Y":
                value = self.get_sample_value(param) if valid else ""
                # 自动替换关联数据
                if '{{' in value and '}}' in value:
                    value = self.replace_relation(value)
                payload.append(f"{param['name']}={value}")
        return "&".join(payload)

    def replace_relation(self, value):
        """替换关联数据"""
        for key in self.relations:
            value = value.replace(f'{{{{{key}}}}}', self.relations[key])
        return value

    def get_sample_value(self, param):
        """生成参数示例值（支持关联数据）"""
        type_map = {
            "string": "test_value",
            "int": "123",
            "boolean": "true",
            "float": "3.14"
        }
        # 特殊字段处理
        if param["name"] == "token":
            return "{{token}}"
        return type_map.get(param["type"].lower(), "test_data")

    def write_excel_data(self, writer, sheet_name, data):
        """写入Excel数据"""
        writer.set_sheet(sheet_name)
        start_row = writer.wb[sheet_name].max_row + 1

        for row_idx, row_data in enumerate(data):
            for col_idx in range(10):  # 对应Excel的A-J列
                value = row_data[col_idx] if col_idx < len(row_data) else ""
                writer.write(start_row + row_idx, col_idx, value)


if __name__ == "__main__":
    root = tk.Tk()
    app = APITestGenerator(root)
    root.mainloop()